package org.suze.demo.config;

import com.alibaba.druid.pool.DruidDataSource;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.suze.demo.route.DataSourceEnum;
import org.suze.demo.route.DynamicRoutingDataSource;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * 数据源相关配置
 */
@Configuration
@Slf4j
@MapperScan(basePackages = {"org.suze.demo.mapper"}, sqlSessionFactoryRef = "sqlSessionFactory")
@EnableConfigurationProperties({DataSourceProperties.class})
public class DynamicDataSourceConfig {


    /**
     * 配置 AbstractRoutingDataSource
     * @return
     */
    @Bean
    public AbstractRoutingDataSource dynamicDataSource(DataSourceProperties ds) {
        DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
        // 添加所有数据源
        Map<Object, Object> map = new HashMap<>(2);
        DataSource defaultDs = dataSource(ds);
        map.put(DataSourceEnum.DEFAULT, defaultDs);
        dynamicRoutingDataSource.setTargetDataSources(map);
        // 设置默认数据源
        dynamicRoutingDataSource.setDefaultTargetDataSource(defaultDs);
        return dynamicRoutingDataSource;
    }

    /**
     * 事务配置
     * @return
     */
    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager(AbstractRoutingDataSource dynamicDataSource) {
        log.info("MybatisConfig -- 事务启动 ~");
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dynamicDataSource);
        transactionManager.setGlobalRollbackOnParticipationFailure(true);
        return transactionManager;
    }

    /**
     * SqlSessionFactory 配置
     * @return
     */
    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactoryBean(AbstractRoutingDataSource dynamicDataSource) {
        log.info("MybatisConfig -- 生成 sqlSessionFactoryBean");
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dynamicDataSource);
        Properties properties = new Properties();
        properties.setProperty("dialect", "mysql");
        bean.setConfigurationProperties(properties);
        try {
            return bean.getObject();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 数据源配置信息
     * @param source
     * @return
     */
    private DataSource dataSource(DataSourceProperties source) {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(source.getDriverClassName());
        dataSource.setUrl(source.getUrl());
        dataSource.setUsername(source.getUsername());
        dataSource.setPassword(source.getPassword());
        //配置初始化大小、最小、最大
        dataSource.setInitialSize(source.getInitialSize());
        dataSource.setMinIdle(source.getMinIdle());
        dataSource.setMaxActive(source.getMaxActive());
        try {
            dataSource.setFilters("stat,slf4j,wall");
        } catch (SQLException e) {
            log.error("datasource 初始化 filters 失败", e);
        }
        dataSource.setLogAbandoned(true);
        dataSource.setValidationQuery("SELECT 1");
        //配置获取连接等待超时的时间
        dataSource.setMaxWait(60000);
        //建议配置为true，不影响性能，并且保证安全性。申请连接的时候检测，如果空闲时间大于timeBetweenEvictionRunsMillis，执行validationQuery检测连接是否有效。
        dataSource.setTestWhileIdle(true);
        //申请连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能。
        dataSource.setTestOnBorrow(false);
        //归还连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能。
        dataSource.setTestOnReturn(false);
        //配置一个连接在池中最小生存的时间，单位是毫秒
        dataSource.setMinEvictableIdleTimeMillis(300000);
        //配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位是毫秒
        dataSource.setTimeBetweenEvictionRunsMillis(60000);
        log.info("MybatisConfig -- DataSource 数据源");
        return dataSource;
    }

}
